﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using MiniJob.Worker.Jobs;
using MiniJob.Worker.Runtime;

namespace Microsoft.AspNetCore.Builder;

public static class MiniJobEndpointRouteBuilderExtensions
{
    public static IEndpointConventionBuilder MapJobsHandlers(this IEndpointRouteBuilder endpoints)
    {
        if (endpoints.ServiceProvider.GetService<MiniJobRuntime>() is null)
        {
            throw new InvalidOperationException(
                "The MiniJobRuntime service is not registered with the dependency injection container. " +
                "Call AddMiniJobs() inside ConfigureServices() to register the miniJob runtime and job types.");
        }

        var builders = new[]
        {
            MapMiniJobConfigEndpoint(endpoints),
            MapMiniJobRunJobEndpoint(endpoints),
            MapMiniJobStopInstanceEndpoint(endpoints),
            MapMiniJobQueryInstanceStatusEndpoint(endpoints),
            MapMiniJobHealthChecks(endpoints)
        };

        return new CompositeEndpointConventionBuilder(builders);
    }

    private static IEndpointConventionBuilder MapMiniJobConfigEndpoint(this IEndpointRouteBuilder endpoints)
    {
        var runtime = endpoints.ServiceProvider.GetRequiredService<MiniJobRuntime>();
        return endpoints.MapGet("worker/config", async context =>
        {
            context.Response.ContentType = "application/json";
            await runtime.SerializeRegisteredJobs(context.Response.BodyWriter);
            await context.Response.BodyWriter.FlushAsync();
        }).WithDisplayName(b => "MiniJob Worker Config");
    }

    private static IEndpointConventionBuilder MapMiniJobRunJobEndpoint(this IEndpointRouteBuilder endpoints)
    {
        var runtime = endpoints.ServiceProvider.GetRequiredService<MiniJobRuntime>();
        return endpoints.MapPost("worker/runJob", async (JobContext jobContext) =>
        {
            await runtime.RunJobAsync(jobContext);
        }).WithDisplayName(b => "MiniJob Worker RunJob");
    }

    private static IEndpointConventionBuilder MapMiniJobStopInstanceEndpoint(this IEndpointRouteBuilder endpoints)
    {
        var runtime = endpoints.ServiceProvider.GetRequiredService<MiniJobRuntime>();
        return endpoints.MapDelete("worker/stopInstance/{instanceId}", async context =>
        {
            var routeValues = context.Request.RouteValues;
            var instanceId = (string)routeValues["instanceId"]!;

            context.Response.ContentType = "application/json";
            await runtime.SerializeQueryStatus(context.Response.BodyWriter, instanceId);
            await context.Response.BodyWriter.FlushAsync();
        }).WithDisplayName(b => "MiniJob Worker Stop Instance");
    }

    private static IEndpointConventionBuilder MapMiniJobQueryInstanceStatusEndpoint(this IEndpointRouteBuilder endpoints)
    {
        var runtime = endpoints.ServiceProvider.GetRequiredService<MiniJobRuntime>();
        return endpoints.MapGet("worker/queryInstanceStatus/{instanceId}", async context =>
        {
            var routeValues = context.Request.RouteValues;
            var instanceId = (string)routeValues["instanceId"]!;

            context.Response.ContentType = "application/json";
            await runtime.SerializeQueryStatus(context.Response.BodyWriter, instanceId);
            await context.Response.BodyWriter.FlushAsync();
        }).WithDisplayName(b => "MiniJob Worker Query InstanceStatus");
    }

    private static IEndpointConventionBuilder MapMiniJobHealthChecks(this IEndpointRouteBuilder endpoints)
    {
        var builder = endpoints.MapHealthChecks("worker/healthz").WithMetadata(new AllowAnonymousAttribute());
        builder.Add(b =>
        {
            ((RouteEndpointBuilder)b).Order = 100;
        });
        return builder.WithDisplayName(b => "MiniJob Worker Health Check");
    }

    private class CompositeEndpointConventionBuilder(IEndpointConventionBuilder[] inner) : IEndpointConventionBuilder
    {
        public void Add(Action<EndpointBuilder> convention)
        {
            for (var i = 0; i < inner.Length; i++)
            {
                inner[i].Add(convention);
            }
        }
    }
}
